package com.hh.myfish.model;

import lombok.Data;

import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 这个是断路器的模型
 */
@Data
public class Fish {

    /**
     * 窗口时间
     */
    public static final Integer WINDOW_TIME = 20;

    /**
     * 最大失败数
     */
    public static final Integer MAX_FAIL_COUNT = 3;

    /**
     * 断路器中有他自己的状态
     */
    private FishStatus status = FishStatus.CLOSE;

    /**
     * 当前这个断路器失败次数
     * i++
     * 保证线程安全
     */
    private AtomicInteger currentFailCount = new AtomicInteger(0);

    /**
     * 线程池
     */
    private ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
            4,
            8,
            30,
            TimeUnit.SECONDS,
            new LinkedBlockingQueue<>(2000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy()
    );

    private Object lock = new Object();

    {
        // 对象被new的时候会执行
        poolExecutor.execute(() -> {
            while (true) {
                try {
                    TimeUnit.SECONDS.sleep(WINDOW_TIME);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 如果断路器是开的 那么不会去调用 不会有失败 就不会计次数 就没有必要清零 不执行
                if (this.status.equals(FishStatus.CLOSE)) {
                    // 清零
                    this.currentFailCount.set(0);
                } else {
                    // 不需要清零 不需要清零 这个线程可以不工作
                    synchronized (lock) {
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        });
    }

    /**
     * 成员方法 记录失败次数
     */
    public void addFailCount() {
        // ++i
        int i = currentFailCount.incrementAndGet();
        // i++
//        currentFailCount.getAndIncrement();
        if (i >= MAX_FAIL_COUNT) {
            // 说明失败次数已经到了阈值
            // 修改当前状态为open
            this.setStatus(FishStatus.OPEN);
            // 当断路器打开后 就不能去访问了 需要将他变成半开
            // 等待一个时间窗口 让断路器变成半开
            poolExecutor.execute(() -> {
                try {
                    TimeUnit.SECONDS.sleep(WINDOW_TIME);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.setStatus(FishStatus.HALF_OPEN);
                // 重置失败次数 不然下次进来直接就会打开断路器
                this.currentFailCount.set(0);
            });
        }
    }
}
